#include "crootwindow.h"
#include "cclient.h"

#include <X11/Xatom.h>
#include <buola/algorithm/container.h>
#include <buola/gui/x11/cx11toolkit.h>
#include <buola/gui/x11/cx11display.h>
#include <buola/gui/x11/cx11window.h>

#include <buola/gui/x11/icccm.h>

namespace buola { namespace bdesktop {

CRootWindow *gRootWindow;

CRootWindow::CRootWindow()
    :   mActiveClient(nullptr),
        mActiveDesktop(0)
{
}

CRootWindow::~CRootWindow()
{
}

void CRootWindow::Create()
{
    CWindow::CreateRoot();
    ((gui::x11::CX11Window*)GetPhysical())->AddFilter(this);
}

void CRootWindow::Init()
{
    SetEvent(gui::EVENT_PROPERTY);

    ReadClientList();
    ReadActiveWindow();
    ReadDesktopInfo();
}

CClient *CRootWindow::CreateClient(Window pID)
{
    CClient *lClient=GetClient(pID);
    if(lClient)
        return lClient;

//Create the CClient
    lClient=new CClient();
    if(!lClient->Create(pID))
    {
        delete lClient;
        return nullptr;
    }

    mClients.push_back(lClient);

    sClientAdded(lClient);

    return lClient;
}

void CRootWindow::RemoveClient(CClient *pClient)
{
    pClient->sRemoved();
    sClientRemoved(pClient);
    if(mActiveClient==pClient)
        mActiveClient=nullptr;
    delete pClient;
}

CClient *CRootWindow::GetClient(Window pID)
{
    for(int i=0;i<mClients.size();i++)
    {
        if((Window)mClients[i]->GetID()==pID)
            return mClients[i];
    }

    return nullptr;
}

void CRootWindow::SwitchToDesktop(int pDesktop)
{
    ((gui::x11::CX11Window*)GetPhysical())->SendClientMessage(this,gui::x11::get_default_x11_display()->Atoms().mNetCurrentDesktop,
                    pDesktop,CurrentTime);
}

void CRootWindow::ReadClientList()
{
    std::vector<CClient*> lOldList=mClients;
    mClients.clear();

    std::vector<Window> lWinList;
    gui::x11::icccm::read_net_client_list_stacking(lWinList);

    for(int i=0;i<lWinList.size();i++)
    {
        if(CWindow::GetWindowForID(lWinList[i]))
            continue;

        bool lFound=false;
        for(int j=0;j<lOldList.size();j++)
        {
            if(!lOldList[j]) continue;
            if(lOldList[j]->GetID()==(TID)lWinList[i])
            {
                mClients.push_back(lOldList[j]);
                lOldList[j]=nullptr;
                lFound=true;
                break;
            }
        }
        if(!lFound)
        {
            CreateClient(lWinList[i]);
        }
    }

    for(int i=0;i<lOldList.size();i++)
    {
        if(lOldList[i])
        {
            RemoveClient(lOldList[i]);
        }
    }

    sStackChange();
}

void CRootWindow::ReadActiveWindow()
{
    Window lWindow=gui::x11::icccm::read_net_active_window();

    CClient *lC=GetClient(lWindow);

    if(mActiveClient!=lC)
    {
        CClient *lOld=mActiveClient;
        mActiveClient=lC;
        sClientActivated(mActiveClient,lOld);
    }
}

void CRootWindow::ReadDesktopInfo()
{
    mDesktops.clear();

    int lDesktopCount=gui::x11::icccm::read_net_number_of_desktops();

    if(lDesktopCount<1)
    {
        sDesktopChange();
        return;
    }

    mActiveDesktop=gui::x11::icccm::read_net_current_desktop();

    mDesktopSize=gui::x11::icccm::read_net_desktop_geometry();

    if(mDesktopSize.IsUndefined())
        mDesktopSize=GetSize();

    std::vector<CIntPoint> lViewPorts;
    gui::x11::icccm::read_net_desktop_viewport(lViewPorts);
    std::vector<CIntRect> lWorkAreas;
    gui::x11::icccm::read_net_workarea(lWorkAreas);
    
    if(lViewPorts.size()<lDesktopCount||lWorkAreas.size()<lDesktopCount)
    {
        sDesktopChange();
        return;
    }
    
    for(int i=0;i<lDesktopCount;i++)
    {
        CWMDesktop &lNew=construct_back(mDesktops);
        lNew.mViewPort=lViewPorts[i];
        lNew.mWorkArea=lWorkAreas[i];
    }

    sDesktopChange();
}

bool CRootWindow::OnX11Event(XEvent *pE)
{
    switch(pE->xany.type)
    {
    case PropertyNotify:
        DoProperty(&pE->xproperty);
    }
    return true;
}

void CRootWindow::DoProperty(XPropertyEvent *pE)
{
    if(pE->atom==gui::x11::get_default_x11_display()->Atoms().mNetClientListStacking)
    {
        ReadClientList();
    }
    else if(pE->atom==gui::x11::get_default_x11_display()->Atoms().mNetClientList)
    {
    }
    else if(pE->atom==gui::x11::get_default_x11_display()->Atoms().mNetActiveWindow)
    {
        ReadActiveWindow();
    }
    else if(pE->atom==gui::x11::get_default_x11_display()->Atoms().mNetNumberOfDesktops||
        pE->atom==gui::x11::get_default_x11_display()->Atoms().mNetDesktopGeometry||
        pE->atom==gui::x11::get_default_x11_display()->Atoms().mNetCurrentDesktop)
    {
        ReadDesktopInfo();
    }
    else
    {
        msg_debug<5>() << "Property changed in root:" << gui::x11::get_default_x11_display()->GetAtomName(pE->atom) << "\n";
    }
}

/*namespace bdesktop*/ } /*namespace buola*/ }
